/***************************************************************************/
/*                                                                         */
/*  ftbitmap.c                                                             */
/*                                                                         */
/*    FreeType utility functions for bitmaps (body).                       */
/*                                                                         */
/*  Copyright 2004, 2005, 2006, 2007, 2008, 2009 by                        */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_BITMAP_H
#include FT_IMAGE_H
#include FT_INTERNAL_OBJECTS_H


static
const FT_Bitmap null_bitmap = { 0, 0, 0, 0, 0, 0, 0, 0 };


/* documentation is in ftbitmap.h */

FT_EXPORT_DEF(void)
FT_Bitmap_New(FT_Bitmap * abitmap)
{
    *abitmap = null_bitmap;
}


/* documentation is in ftbitmap.h */

FT_EXPORT_DEF(FT_Error)
FT_Bitmap_Copy(FT_Library library,
               const FT_Bitmap * source,
               FT_Bitmap * target)
{
    FT_Memory memory = library->memory;
    FT_Error error = FT_Err_Ok;
    FT_Int pitch = source->pitch;
    FT_ULong size;


    if (source == target)
        return FT_Err_Ok;

    if (source->buffer == NULL)
    {
        *target = *source;

        return FT_Err_Ok;
    }

    if (pitch < 0)
        pitch = -pitch;
    size = (FT_ULong)(pitch * source->rows);

    if (target->buffer)
    {
        FT_Int target_pitch = target->pitch;
        FT_ULong target_size;


        if (target_pitch < 0)
            target_pitch = -target_pitch;
        target_size = (FT_ULong)(target_pitch * target->rows);

        if (target_size != size)
            FT_QREALLOC(target->buffer, target_size, size);
    }
    else
        FT_QALLOC(target->buffer, size);

    if (!error)
    {
        unsigned char* p;


        p = target->buffer;
        *target = *source;
        target->buffer = p;

        FT_MEM_COPY(target->buffer, source->buffer, size);
    }

    return error;
}


static FT_Error
ft_bitmap_assure_buffer(FT_Memory memory,
                        FT_Bitmap* bitmap,
                        FT_UInt xpixels,
                        FT_UInt ypixels)
{
    FT_Error error;
    int pitch;
    int new_pitch;
    FT_UInt bpp;
    FT_Int i, width, height;
    unsigned char* buffer;


    width = bitmap->width;
    height = bitmap->rows;
    pitch = bitmap->pitch;
    if (pitch < 0)
        pitch = -pitch;

    switch (bitmap->pixel_mode)
    {
        case FT_PIXEL_MODE_MONO:
            bpp = 1;
            new_pitch = (width + xpixels + 7) >> 3;
            break;
        case FT_PIXEL_MODE_GRAY2:
            bpp = 2;
            new_pitch = (width + xpixels + 3) >> 2;
            break;
        case FT_PIXEL_MODE_GRAY4:
            bpp = 4;
            new_pitch = (width + xpixels + 1) >> 1;
            break;
        case FT_PIXEL_MODE_GRAY:
        case FT_PIXEL_MODE_LCD:
        case FT_PIXEL_MODE_LCD_V:
            bpp = 8;
            new_pitch = (width + xpixels);
            break;
        default:
            return FT_Err_Invalid_Glyph_Format;
    }

    /* if no need to allocate memory */
    if (ypixels == 0 && new_pitch <= pitch)
    {
        /* zero the padding */
        FT_Int bit_width = pitch * 8;
        FT_Int bit_last = (width + xpixels) * bpp;


        if (bit_last < bit_width)
        {
            FT_Byte* line = bitmap->buffer + (bit_last >> 3);
            FT_Byte* end = bitmap->buffer + pitch;
            FT_Int shift = bit_last & 7;
            FT_UInt mask = 0xFF00U >> shift;
            FT_Int count = height;


            for (; count > 0; count--, line += pitch, end += pitch)
            {
                FT_Byte* write = line;


                if (shift > 0)
                {
                    write[0] = (FT_Byte)(write[0] & mask);
                    write++;
                }
                if (write < end)
                    FT_MEM_ZERO(write, end - write);
            }
        }

        return FT_Err_Ok;
    }

    if (FT_QALLOC_MULT(buffer, new_pitch, bitmap->rows + ypixels))
        return error;

    if (bitmap->pitch > 0)
    {
        FT_Int len = (width * bpp + 7) >> 3;


        for (i = 0; i < bitmap->rows; i++)
            FT_MEM_COPY(buffer + new_pitch * (ypixels + i),
                        bitmap->buffer + pitch * i, len);
    }
    else
    {
        FT_Int len = (width * bpp + 7) >> 3;


        for (i = 0; i < bitmap->rows; i++)
            FT_MEM_COPY(buffer + new_pitch * i,
                        bitmap->buffer + pitch * i, len);
    }

    FT_FREE(bitmap->buffer);
    bitmap->buffer = buffer;

    if (bitmap->pitch < 0)
        new_pitch = -new_pitch;

    /* set pitch only, width and height are left untouched */
    bitmap->pitch = new_pitch;

    return FT_Err_Ok;
}


/* documentation is in ftbitmap.h */

FT_EXPORT_DEF(FT_Error)
FT_Bitmap_Embolden(FT_Library library,
                   FT_Bitmap * bitmap,
                   FT_Pos xStrength,
                   FT_Pos yStrength)
{
    FT_Error error;
    unsigned char* p;
    FT_Int i, x, y, pitch;
    FT_Int xstr, ystr;


    if (!library)
        return FT_Err_Invalid_Library_Handle;

    if (!bitmap || !bitmap->buffer)
        return FT_Err_Invalid_Argument;

    if (((FT_PIX_ROUND(xStrength) >> 6) > FT_INT_MAX) ||
        ((FT_PIX_ROUND(yStrength) >> 6) > FT_INT_MAX))
        return FT_Err_Invalid_Argument;

    xstr = (FT_Int)FT_PIX_ROUND(xStrength) >> 6;
    ystr = (FT_Int)FT_PIX_ROUND(yStrength) >> 6;

    if (xstr == 0 && ystr == 0)
        return FT_Err_Ok;
    else if (xstr < 0 || ystr < 0)
        return FT_Err_Invalid_Argument;

    switch (bitmap->pixel_mode)
    {
        case FT_PIXEL_MODE_GRAY2:
        case FT_PIXEL_MODE_GRAY4:
        {
            FT_Bitmap tmp;
            FT_Int align;


            if (bitmap->pixel_mode == FT_PIXEL_MODE_GRAY2)
                align = (bitmap->width + xstr + 3) / 4;
            else
                align = (bitmap->width + xstr + 1) / 2;

            FT_Bitmap_New(&tmp);

            error = FT_Bitmap_Convert(library, bitmap, &tmp, align);
            if (error)
                return error;

            FT_Bitmap_Done(library, bitmap);
            *bitmap = tmp;
        }
        break;

        case FT_PIXEL_MODE_MONO:
            if (xstr > 8)
                xstr = 8;
            break;

        case FT_PIXEL_MODE_LCD:
            xstr *= 3;
            break;

        case FT_PIXEL_MODE_LCD_V:
            ystr *= 3;
            break;
    }

    error = ft_bitmap_assure_buffer(library->memory, bitmap, xstr, ystr);
    if (error)
        return error;

    pitch = bitmap->pitch;
    if (pitch > 0)
        p = bitmap->buffer + pitch * ystr;
    else
    {
        pitch = -pitch;
        p = bitmap->buffer + pitch * (bitmap->rows - 1);
    }

    /* for each row */
    for (y = 0; y < bitmap->rows; y++)
    {
        /*
         * Horizontally:
         *
         * From the last pixel on, make each pixel or'ed with the
         * `xstr' pixels before it.
         */
        for (x = pitch - 1; x >= 0; x--)
        {
            unsigned char tmp;


            tmp = p[x];
            for (i = 1; i <= xstr; i++)
            {
                if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO)
                {
                    p[x] |= tmp >> i;

                    /* the maximum value of 8 for `xstr' comes from here */
                    if (x > 0)
                        p[x] |= p[x - 1] << (8 - i);

                    #if 0
                    if (p[x] == 0xff)
                        break;
                    #endif
                }
                else
                {
                    if (x - i >= 0)
                    {
                        if (p[x] + p[x - i] > bitmap->num_grays - 1)
                        {
                            p[x] = (unsigned char)(bitmap->num_grays - 1);
                            break;
                        }
                        else
                        {
                            p[x] = (unsigned char)(p[x] + p[x - i]);
                            if (p[x] == bitmap->num_grays - 1)
                                break;
                        }
                    }
                    else
                        break;
                }
            }
        }

        /*
         * Vertically:
         *
         * Make the above `ystr' rows or'ed with it.
         */
        for (x = 1; x <= ystr; x++)
        {
            unsigned char* q;


            q = p - bitmap->pitch * x;
            for (i = 0; i < pitch; i++)
                q[i] |= p[i];
        }

        p += bitmap->pitch;
    }

    bitmap->width += xstr;
    bitmap->rows += ystr;

    return FT_Err_Ok;
}


/* documentation is in ftbitmap.h */

FT_EXPORT_DEF(FT_Error)
FT_Bitmap_Convert(FT_Library library,
                  const FT_Bitmap * source,
                  FT_Bitmap * target,
                  FT_Int alignment)
{
    FT_Error error = FT_Err_Ok;
    FT_Memory memory;


    if (!library)
        return FT_Err_Invalid_Library_Handle;

    memory = library->memory;

    switch (source->pixel_mode)
    {
        case FT_PIXEL_MODE_MONO:
        case FT_PIXEL_MODE_GRAY:
        case FT_PIXEL_MODE_GRAY2:
        case FT_PIXEL_MODE_GRAY4:
        case FT_PIXEL_MODE_LCD:
        case FT_PIXEL_MODE_LCD_V:
        {
            FT_Int pad;
            FT_Long old_size;


            old_size = target->rows * target->pitch;
            if (old_size < 0)
                old_size = -old_size;

            target->pixel_mode = FT_PIXEL_MODE_GRAY;
            target->rows = source->rows;
            target->width = source->width;

            pad = 0;
            if (alignment > 0)
            {
                pad = source->width % alignment;
                if (pad != 0)
                    pad = alignment - pad;
            }

            target->pitch = source->width + pad;

            if (target->rows * target->pitch > old_size &&
                FT_QREALLOC(target->buffer,
                            old_size, target->rows * target->pitch))
                return error;
        }
        break;

        default:
            error = FT_Err_Invalid_Argument;
    }

    switch (source->pixel_mode)
    {
        case FT_PIXEL_MODE_MONO:
        {
            FT_Byte* s = source->buffer;
            FT_Byte* t = target->buffer;
            FT_Int i;


            target->num_grays = 2;

            for (i = source->rows; i > 0; i--)
            {
                FT_Byte* ss = s;
                FT_Byte* tt = t;
                FT_Int j;


                /* get the full bytes */
                for (j = source->width >> 3; j > 0; j--)
                {
                    FT_Int val = ss[0]; /* avoid a byte->int cast on each line */


                    tt[0] = (FT_Byte)((val & 0x80) >> 7);
                    tt[1] = (FT_Byte)((val & 0x40) >> 6);
                    tt[2] = (FT_Byte)((val & 0x20) >> 5);
                    tt[3] = (FT_Byte)((val & 0x10) >> 4);
                    tt[4] = (FT_Byte)((val & 0x08) >> 3);
                    tt[5] = (FT_Byte)((val & 0x04) >> 2);
                    tt[6] = (FT_Byte)((val & 0x02) >> 1);
                    tt[7] = (FT_Byte)(val & 0x01);

                    tt += 8;
                    ss += 1;
                }

                /* get remaining pixels (if any) */
                j = source->width & 7;
                if (j > 0)
                {
                    FT_Int val = *ss;


                    for (; j > 0; j--)
                    {
                        tt[0] = (FT_Byte)((val & 0x80) >> 7);
                        val <<= 1;
                        tt += 1;
                    }
                }

                s += source->pitch;
                t += target->pitch;
            }
        }
        break;


        case FT_PIXEL_MODE_GRAY:
        case FT_PIXEL_MODE_LCD:
        case FT_PIXEL_MODE_LCD_V:
        {
            FT_Int width = source->width;
            FT_Byte* s = source->buffer;
            FT_Byte* t = target->buffer;
            FT_Int s_pitch = source->pitch;
            FT_Int t_pitch = target->pitch;
            FT_Int i;


            target->num_grays = 256;

            for (i = source->rows; i > 0; i--)
            {
                FT_ARRAY_COPY(t, s, width);

                s += s_pitch;
                t += t_pitch;
            }
        }
        break;


        case FT_PIXEL_MODE_GRAY2:
        {
            FT_Byte* s = source->buffer;
            FT_Byte* t = target->buffer;
            FT_Int i;


            target->num_grays = 4;

            for (i = source->rows; i > 0; i--)
            {
                FT_Byte* ss = s;
                FT_Byte* tt = t;
                FT_Int j;


                /* get the full bytes */
                for (j = source->width >> 2; j > 0; j--)
                {
                    FT_Int val = ss[0];


                    tt[0] = (FT_Byte)((val & 0xC0) >> 6);
                    tt[1] = (FT_Byte)((val & 0x30) >> 4);
                    tt[2] = (FT_Byte)((val & 0x0C) >> 2);
                    tt[3] = (FT_Byte)((val & 0x03));

                    ss += 1;
                    tt += 4;
                }

                j = source->width & 3;
                if (j > 0)
                {
                    FT_Int val = ss[0];


                    for (; j > 0; j--)
                    {
                        tt[0] = (FT_Byte)((val & 0xC0) >> 6);
                        val <<= 2;
                        tt += 1;
                    }
                }

                s += source->pitch;
                t += target->pitch;
            }
        }
        break;


        case FT_PIXEL_MODE_GRAY4:
        {
            FT_Byte* s = source->buffer;
            FT_Byte* t = target->buffer;
            FT_Int i;


            target->num_grays = 16;

            for (i = source->rows; i > 0; i--)
            {
                FT_Byte* ss = s;
                FT_Byte* tt = t;
                FT_Int j;


                /* get the full bytes */
                for (j = source->width >> 1; j > 0; j--)
                {
                    FT_Int val = ss[0];


                    tt[0] = (FT_Byte)((val & 0xF0) >> 4);
                    tt[1] = (FT_Byte)((val & 0x0F));

                    ss += 1;
                    tt += 2;
                }

                if (source->width & 1)
                    tt[0] = (FT_Byte)((ss[0] & 0xF0) >> 4);

                s += source->pitch;
                t += target->pitch;
            }
        }
        break;


        default:
            ;
    }

    return error;
}


/* documentation is in ftbitmap.h */

FT_EXPORT_DEF(FT_Error)
FT_GlyphSlot_Own_Bitmap(FT_GlyphSlot slot)
{
    if (slot && slot->format == FT_GLYPH_FORMAT_BITMAP &&
        !(slot->internal->flags & FT_GLYPH_OWN_BITMAP))
    {
        FT_Bitmap bitmap;
        FT_Error error;


        FT_Bitmap_New(&bitmap);
        error = FT_Bitmap_Copy(slot->library, &slot->bitmap, &bitmap);
        if (error)
            return error;

        slot->bitmap = bitmap;
        slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
    }

    return FT_Err_Ok;
}


/* documentation is in ftbitmap.h */

FT_EXPORT_DEF(FT_Error)
FT_Bitmap_Done(FT_Library library,
               FT_Bitmap * bitmap)
{
    FT_Memory memory;


    if (!library)
        return FT_Err_Invalid_Library_Handle;

    if (!bitmap)
        return FT_Err_Invalid_Argument;

    memory = library->memory;

    FT_FREE(bitmap->buffer);
    *bitmap = null_bitmap;

    return FT_Err_Ok;
}


/* END */